#include "Pch.h"
#include "Config.h"

Config *config;

static const wxChar *colorStrings[] = {
	_T("Default"),
	_T("Hydrogen"),
	_T("Oxygen"),
	_T("Chlorine"),
	_T("Nitrogen"),
	_T("Carbon"),
	_T("Sulphur"),
	_T("Phosphorus"),

	_T("Other"),
	_T("Alpha"),
	_T("Beta"),
	_T("DNA"),

	_T("Background"),
	_T("Selection"),
	_T("Last Selection"),
	_T("3D Text"),

	_T("Text"),
	_T("Text BG"),
	_T("Hidden Residue Text"),

	_T("Odd Column BG"),
	_T("Chain Name BG"),

	_T("Selection Text"),
	_T("Selection Text BG"),

	_T("Last Selection Text"),
	_T("Last Selection Text BG"),

	_T("Hydrogen Bonds"),
};

static const wxChar *intStrings[] = {
	_T("Default Width"),
	_T("Default Height"),
	_T("Show Residue Window"),
	_T("Residue Window Pos"),
	_T("Show Sequence Window"),
	_T("Sequence Window Pos"),
	_T("Rotation Mode"),
	_T("Alignment Flags"),
	_T("Web in New Window"),
	_T("Open GL Draw Mode"),
	_T("Cartoon Resolution"),
	_T("Sphere Resolution"),
	_T("Cylinder Resolution"),
};

// Note:  Have to duplicate default value, as it may be the same as value.
bool ReadWriteString(wxConfigBase &cfg, const wxString &key, wxString *value, const wxString defaultVal, int load) {
	if (load) return cfg.Read(key, value, defaultVal);
	return cfg.Write(key, *value);
}

bool ReadWriteInt(wxConfigBase &cfg, const wxString &key, int *value, int defaultVal, int load) {
	if (load) return cfg.Read(key, value, defaultVal);
	return cfg.Write(key, *value);
}

bool ReadWriteInt(wxConfigBase &cfg, const wxString &key, long *value, long defaultVal, int load) {
	if (load) return cfg.Read(key, value, defaultVal);
	return cfg.Write(key, *value);
}

void Config::LoadSave(int load) {
	if (load) {
		SetDefault();
	}
	wxStandardPathsBase &paths (wxStandardPaths::Get());
	wxString docs = paths.GetUserLocalDataDir();
	wxFileName path;
	path.AssignDir(docs);
	path.Mkdir(0777, wxPATH_MKDIR_FULL);
	path.SetFullName(_T("Molli.cfg"));

	wxString test = path.GetFullPath();

	wxFileConfig cfg(_T("Molli"), wxEmptyString, test);
	ReadWriteString(cfg, _T("PDB Path"), &strings.pdbPath, strings.pdbPath, load);
	ReadWriteString(cfg, _T("PDB URL"), &strings.pdbUrl, strings.pdbUrl, load);
	ReadWriteString(cfg, _T("ASTRAL Path"), &strings.astralPath, strings.astralPath, load);
	ReadWriteString(cfg, _T("ASTRAL URL"), &strings.astralUrl, strings.astralUrl, load);
	ReadWriteString(cfg, _T("Last Web"), &strings.lastOpenWeb, strings.lastOpenWeb, load);

	for (int i=0; i<NUM_COLORS; i++) {
		ReadWriteInt(cfg, wxString::Format(_T("Color %s"), colorStrings[i]), &firstColor[i].uVal, firstColor[i].uVal, load);
	}
	for (int i=0; i<NUM_INTS; i++) {
		ReadWriteInt(cfg, intStrings[i], &firstInt[i], firstInt[i], load);
	}

	if (drawMode >= NUM_DRAW_MODES) {
		drawMode = DRAW_MODE_DISPLAY_LISTS;
	}
}

void Config::SetDefault() {
	wxStandardPathsBase &paths (wxStandardPaths::Get());
	wxString docs = paths.GetDocumentsDir();
	wxFileName path;
	path.AssignDir(docs);
	path.AppendDir(_T("Molli"));
	wxFileName path2(path);
	path2.AppendDir(_T("pdb"));

	strings.pdbPath = path2.GetFullPath();
	strings.pdbUrl = _T("http://www.rcsb.org/pdb/files/%s.pdb.gz");
	path2 = path;
	path2.AppendDir(_T("Astral"));
	strings.astralPath = path2.GetFullPath();
	strings.astralUrl = _T("http://astral.berkeley.edu/pdbstyle.cgi?id=%s&output=text");

	static const Color defaultColors[] = {
		{170,   0, 170, 255}, // Default
		{255, 255, 255, 255}, // Hydrogen
		{255,   0,   0, 255}, // Oxygen
		{  0, 255,   0, 255}, // Chlorine
		{ 30,  30, 255, 255}, // Nitrogen
		{150, 150, 150, 255}, // Carbon
		{255, 190,   0, 255}, // Sulphur
		{255, 128,   0, 255}, // Phosphorus

		{255, 255, 255, 255}, // Default
		{255,   0,   0, 255}, // Alpha
		{  0,   0, 255, 255}, // Beta
		{255, 170,   0, 255}, // DNA

		{  0,   0,   0, 255}, // 3DBgColor;
		{  0, 255, 205, 255}, // selColor;
		{ 55, 165,   0, 255}, // selRecentColor;
		{128, 128, 255, 255}, // colorText3D;

		{  0,   0,   0, 255}, // textColor;
		{255, 255, 255, 255}, // textBgColor;
		{140, 140, 140, 255}, // textHidden;

		{236, 236, 236, 255}, // textBgColor2;
		{216, 236, 255, 255}, // textBgColorChain;

		{255, 255, 255, 255}, // textSelColor;
		{ 10,  36, 106, 255}, // textSelBgColor;
		{255, 255, 255, 255}, // textSelRecentColor;
		{ 36, 106,  10, 255}, // textSelRecentBgColor;

		{ 50, 150, 255, 255}, // Hbond
	};

	for (int i=0; i<NUM_COLORS; i++) {
		firstColor[i].uVal = defaultColors[i].uVal;
	}

	for (int i=0; i<NUM_INTS; i++) {
		firstInt[i] = 0;
	}

	defaultWidth = 640;
	defaultHeight = 480;
	showResidues = 1;
	residueSplitterPos = -110;
	showSequences = 1;
	sequenceSplitterPos = -110;
	rotationMode = 0;
	drawMode = DRAW_MODE_DISPLAY_LISTS;
	UseQualityPreset(ID_MEDIUM_QUALITY);
}

void Config::UseQualityPreset(unsigned int quality) {
	switch(quality) {
		case ID_LOW_QUALITY:
			cartoonRes = 5;
			cylRes = 12;
			sphereRes = 5;
		break;
		case ID_MEDIUM_QUALITY:
		default:
			cartoonRes = 10;
			cylRes = 20;
			sphereRes = 8;
		break;
		case ID_HIGH_QUALITY:
			cartoonRes = 20;
			cylRes = 20;
			sphereRes = 12;
		break;
	}
}

Config::Config() {
	firstString = &strings.pdbPath;
	firstColor = atomicColors;
}

Config& Config::operator=(const Config& cfg) {
	for (int i=0; i<NUM_CONFIG_STRINGS; i++) {
		firstString[i] = cfg.firstString[i];
	}
	for (int i=0; i<NUM_COLORS; i++) {
		firstColor[i] = cfg.firstColor[i];
	}
	for (int i=0; i<NUM_INTS; i++) {
		firstInt[i] = cfg.firstInt[i];
	}
	return *this;
}

class ConfigDialog : public wxPropertySheetDialog {
public:
	wxPanel* colorPanel;
	wxPanel* pathPanel;
	wxPanel* displayPanel;
	Config m_config;
	wxSpinCtrl *cartoonRes, *sphereRes, *cylRes;
	// Needed because contrary to spec, get text change messages on control creation with null strings.
	// Could just read all controls on dialog close instead, but then I'd have to enumerate all controls
	// by type.
	int creating;

	ConfigDialog(Config &config) {
		m_config = config;
	}

	wxSizer *CreatePathSizer(wxWindow *parent, int id, const wxString &prompt) {
		wxBoxSizer *sizer = new wxBoxSizer(wxHORIZONTAL);
		sizer->Add(new wxStaticText(parent, wxID_ANY, prompt), 0, wxALIGN_CENTER_VERTICAL);
		sizer->Add(10, 0);
		wxDirPickerCtrl *w = new wxDirPickerCtrl(parent, id, m_config.firstString[id], wxDirSelectorPromptStr, wxDefaultPosition, wxDefaultSize, wxDIRP_DEFAULT_STYLE & ~wxDIRP_DIR_MUST_EXIST);
		// For whatever reason, have to do it this way, unless I hard code height (Using -1 doesn't work).
		wxSize s = w->GetEffectiveMinSize();
		s.SetWidth(350);
		w->SetInitialSize(s);
		sizer->Add(w, 1, wxEXPAND);
		return sizer;
	}

	wxSizer *CreateLabeledSizer(wxWindow *parent, wxControl *ctrl, const wxString &prompt, unsigned int flags) {
		wxBoxSizer *sizer = new wxBoxSizer(wxHORIZONTAL);
		sizer->Add(new wxStaticText(parent, wxID_ANY, prompt), 0, wxALIGN_CENTER_VERTICAL);
		sizer->Add(10, 0);
		sizer->Add(ctrl, 1, flags);
		return sizer;
	}

	wxSizer *CreateTextSizer(wxWindow *parent, int id, const wxString &prompt) {
		wxTextCtrl *textCtrl = new wxTextCtrl(parent, id, m_config.firstString[id], wxDefaultPosition, wxSize(350, -1));
		textCtrl->SetMaxLength(512);
		return CreateLabeledSizer(parent, textCtrl, prompt, wxEXPAND);
	}

	wxSizer *CreateColorSizer(wxWindow *parent, int i, int count) {
		if (count <= 0) {
			wxBoxSizer *sizer = new wxBoxSizer(wxHORIZONTAL);
			sizer->Add(5, 0);
			sizer->Add(new wxColourPickerCtrl(parent, NUM_CONFIG_STRINGS+i, wxColour(m_config.firstColor[i].r, m_config.firstColor[i].g, m_config.firstColor[i].b)));
			sizer->Add(8, 0);
			sizer->Add(new wxStaticText(parent, wxID_ANY, colorStrings[i]), 0, wxALIGN_CENTER_VERTICAL);
			sizer->Add(5, 0);
			return sizer;
		}
		else {
			wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
			for (int j=0; j<count; j++) {
				sizer->Add(0, 4);
				sizer->Add(CreateColorSizer(parent, i+j, 0));
				sizer->Add(0, 4);
			}
			return sizer;
		}
	}

	/*
	wxSizer *CreateButtonSizer(wxWindow *parent, unsigned int id, wxString text) {
		wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
		sizer->Add(0, 0, 1, wxEXPAND);
		sizer->Add(new wxButton(parent, id, text), 0, wxALIGN_CENTER_VERTICAL);
		return sizer;
	}//*/

	void CreateColorPanel() {
		colorPanel = new wxPanel(GetBookCtrl(), wxID_ANY);
		wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL);


		wxStaticBoxSizer *structureColorSizer = new wxStaticBoxSizer(wxHORIZONTAL, colorPanel, _T("Secondary Structure Colors"));
		wxStaticBoxSizer *atomColorSizer = new wxStaticBoxSizer(wxHORIZONTAL, colorPanel, _T("Atom Colors"));
		wxStaticBoxSizer *textColorSizer = new wxStaticBoxSizer(wxHORIZONTAL, colorPanel, _T("Text Colors"));
		wxStaticBoxSizer *renderColorSizer = new wxStaticBoxSizer(wxHORIZONTAL, colorPanel, _T("3D Colors"));
		for (int i=0; i<4; i++) {
			wxSizer *items[4];
			memset(items, 0, sizeof(items));
			//if (i < 3)
				renderColorSizer->Add(items[0] = CreateColorSizer(colorPanel, ID_COLOR_3D_BG+i, 1));
			structureColorSizer->Add(items[1] = CreateColorSizer(colorPanel, ID_SECONDARY_COLOR_DEFAULT+i, 1));
			atomColorSizer->Add(items[2] = CreateColorSizer(colorPanel, ID_ATOM_COLOR_DEFAULT+2*i, 2));
			if (!i)
				textColorSizer->Add(items[3] = CreateColorSizer(colorPanel, ID_COLOR_TEXT, 3));
			else
				textColorSizer->Add(items[3] = CreateColorSizer(colorPanel, ID_COLOR_TEXT+2*i+1, 2));
			int max = 0;
			for (int j=0; j<sizeof(items)/sizeof(items[0]); j++) {
				if (!items[j]) continue;
				int width = items[j]->GetMinSize().GetWidth();
				if (width > max) max = width;
			}
			for (int j=0; j<sizeof(items)/sizeof(items[0]); j++) {
				if (!items[j]) continue;
				items[j]->SetMinSize(max, -1);
			}
		}

		vsizer->Add(0, 8);
		vsizer->Add(structureColorSizer);
		vsizer->Add(0, 8);
		vsizer->Add(atomColorSizer);
		vsizer->Add(0, 8);
		vsizer->Add(textColorSizer);
		vsizer->Add(0, 8);

		vsizer->Add(renderColorSizer);
		vsizer->Add(0, 8);

		vsizer->Add(CreateColorSizer(colorPanel, ID_COLOR_Hbond, 1));

		vsizer->Add(0, 8);
		vsizer->Add(new wxButton(colorPanel, ID_USE_DEFAULT_COLORS, _T("Use Default")));
		/*wxBoxSizer *renderColorSizer2 = new wxBoxSizer(wxHORIZONTAL);
		renderColorSizer2->Add(renderColorSizer);
		renderColorSizer2->Add(5, 5, 1, wxEXPAND);
		renderColorSizer2->Add(new wxButton(colorPanel, ID_USE_DEFAULT_COLORS, _T("Use Default")), 1, wxALIGN_BOTTOM | wxALIGN_RIGHT);

		vsizer->Add(renderColorSizer2);
		//*/
		vsizer->Add(0, 8);

		wxBoxSizer *hsizer = new wxBoxSizer(wxHORIZONTAL);
		hsizer->Add(10, 0);
		hsizer->Add(vsizer, 0);
		hsizer->Add(10, 0);

		colorPanel->SetSizer(hsizer);
		hsizer->Fit(colorPanel);
		GetBookCtrl()->AddPage(colorPanel, _T("Colors"));
	}

	void CreateDisplayPanel() {
		displayPanel = new wxPanel(GetBookCtrl(), wxID_ANY);
		wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL);

		wxRadioButton *buttons[NUM_DRAW_MODES];

		vsizer->Add(0, 8);
		vsizer->Add(new wxStaticText(displayPanel, wxID_ANY, _T("Render Mode")), 0, wxALIGN_CENTER_VERTICAL);
		vsizer->Add(0, 8);
		vsizer->Add(buttons[DRAW_MODE_SLOW]=new wxRadioButton(displayPanel, ID_DRAW_MODE_BASE+DRAW_MODE_SLOW, _T("Vertex - Slowest on graphics hardware, may be needed with Mesa software drivers to avoid color glitches"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP), 0, wxALIGN_CENTER_VERTICAL);
		vsizer->Add(0, 6);
		vsizer->Add(buttons[DRAW_MODE_INTERLEAVED_ARRAYS]=new wxRadioButton(displayPanel, ID_DRAW_MODE_BASE+DRAW_MODE_INTERLEAVED_ARRAYS, _T("Vertex buffer arrays (VBAs) - Generally significantly faster")), 0, wxALIGN_CENTER_VERTICAL);
		vsizer->Add(0, 6);
		vsizer->Add(buttons[DRAW_MODE_DISPLAY_LISTS]=new wxRadioButton(displayPanel, ID_DRAW_MODE_BASE+DRAW_MODE_DISPLAY_LISTS, _T("Display Lists - Usually the fastest.  Can use significant amounts of memory with some display adapters")), 0, wxALIGN_CENTER_VERTICAL);
		vsizer->Add(0, 8);

		buttons[m_config.drawMode]->SetValue(1);

		wxSize spinSize(50, -1);

		cartoonRes = new wxSpinCtrl(displayPanel, ID_CARTOON_RES, wxEmptyString, wxDefaultPosition, spinSize, wxSP_ARROW_KEYS, 4, 80, m_config.cartoonRes);
		vsizer->Add(CreateLabeledSizer(displayPanel, cartoonRes, _T("Cartoon Quality"), 0));
		cartoonRes->SetValue(m_config.cartoonRes);
		vsizer->Add(0, 8);

		sphereRes = new wxSpinCtrl(displayPanel, ID_SPHERE_RES, wxEmptyString, wxDefaultPosition, spinSize, wxSP_ARROW_KEYS, 4, 20, m_config.cartoonRes);
		vsizer->Add(CreateLabeledSizer(displayPanel, sphereRes, _T("Sphere Quality"), 0));
		sphereRes->SetValue(m_config.sphereRes);
		vsizer->Add(0, 8);

		cylRes = new wxSpinCtrl(displayPanel, ID_CYL_RES, wxEmptyString, wxDefaultPosition, spinSize, wxSP_ARROW_KEYS, 4, 40, m_config.cartoonRes);
		vsizer->Add(CreateLabeledSizer(displayPanel, cylRes, _T("Cylinder Quality"), 0));
		cylRes->SetValue(m_config.cylRes);

		wxBoxSizer *hsizer = new wxBoxSizer(wxHORIZONTAL);
		hsizer->Add(10, 0);
		hsizer->Add(vsizer, 0);
		hsizer->Add(10, 0);

		displayPanel->SetSizer(hsizer);
		hsizer->Fit(displayPanel);
		GetBookCtrl()->AddPage(displayPanel, _T("Display"));
	}

	void CreatePathPanel() {
		pathPanel = new wxPanel(GetBookCtrl(), wxID_ANY);
		wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL);
		wxSizer *items[4];
		vsizer->Add(0, 8);
		vsizer->Add(items[0]=CreatePathSizer(pathPanel, ID_PDB_PATH, _T("PDB Cache path:")), 0, wxEXPAND);
		vsizer->Add(0, 6);
		vsizer->Add(items[1]=CreateTextSizer(pathPanel, ID_PDB_URL, _T("PDB URL:")), 0, wxEXPAND);
		vsizer->Add(0, 10);
		vsizer->Add(items[2]=CreatePathSizer(pathPanel, ID_ASTRAL_PATH, _T("ASTRAL Cache path:")), 0, wxEXPAND);
		vsizer->Add(0, 6);
		vsizer->Add(items[3]=CreateTextSizer(pathPanel, ID_ASTRAL_URL, _T("ASTRAL URL:")), 0, wxEXPAND);
		vsizer->Add(0, 8);
		int max = 0;
		for (int j=0; j<sizeof(items)/sizeof(items[0]); j++) {
			int width = items[j]->GetMinSize().GetWidth();
			if (width > max) max = width;
		}
		for (int j=0; j<sizeof(items)/sizeof(items[0]); j++) {
			items[j]->SetMinSize(max, -1);
		}

		wxBoxSizer *hsizer = new wxBoxSizer(wxHORIZONTAL);
		hsizer->Add(10, 0);
		hsizer->Add(vsizer, 1, wxEXPAND);
		hsizer->Add(10, 0);

		pathPanel->SetSizer(hsizer);
		hsizer->Fit(pathPanel);
		GetBookCtrl()->AddPage(pathPanel, _T("Paths"));
	}

	bool Create(wxWindow *parent) {
		creating = 1;
		if (!wxPropertySheetDialog::Create(parent, wxID_ANY, APP_NAME _T(" Settings"))) return 0;

		// Todo:  Add wxAPPLY if supported.
		CreateButtons(wxOK|wxCANCEL);

		CreateColorPanel();
		CreatePathPanel();
		CreateDisplayPanel();

        LayoutDialog();
		creating = 0;
		return true;
	}

	void OnDirChange(wxFileDirPickerEvent& evt);
	void OnTextChange(wxCommandEvent& evt);
	void OnColorChange(wxColourPickerEvent& evt);
	void OnUseDefault(wxCommandEvent& evt);
	void OnRadioButton(wxCommandEvent& evt);

	void OnChangeRes(wxSpinEvent &evt);

	DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE(ConfigDialog, wxPropertySheetDialog)
	EVT_DIRPICKER_CHANGED(wxID_ANY, ConfigDialog::OnDirChange)
	EVT_TEXT(wxID_ANY, ConfigDialog::OnTextChange)
	EVT_COLOURPICKER_CHANGED(wxID_ANY, ConfigDialog::OnColorChange)
	EVT_BUTTON(ID_USE_DEFAULT_COLORS, ConfigDialog::OnUseDefault)
	EVT_RADIOBUTTON(wxID_ANY, ConfigDialog::OnRadioButton)
	EVT_SPINCTRL(ID_CARTOON_RES, ConfigDialog::OnChangeRes)
	EVT_SPINCTRL(ID_SPHERE_RES, ConfigDialog::OnChangeRes)
	EVT_SPINCTRL(ID_CYL_RES, ConfigDialog::OnChangeRes)
END_EVENT_TABLE()

void ConfigDialog::OnDirChange(wxFileDirPickerEvent& evt) {
	int index = evt.GetId();
	if (index >= 0 && index < NUM_CONFIG_STRINGS && !creating)
		m_config.firstString[index] = evt.GetPath();
}

void ConfigDialog::OnTextChange(wxCommandEvent& evt) {
	int index = evt.GetId();
	if (index >= 0 && index < NUM_CONFIG_STRINGS && !creating)
		m_config.firstString[index] = evt.GetString();
}

void ConfigDialog::OnColorChange(wxColourPickerEvent& evt) {
	int index = evt.GetId()-NUM_CONFIG_STRINGS;
	if (index >= 0 && index < NUM_COLORS && !creating)
		m_config.firstColor[index] = evt.GetColour();
}

void ConfigDialog::OnRadioButton(wxCommandEvent& evt) {
	int id = evt.GetId();
	if (id >= ID_DRAW_MODE_BASE && id < ID_DRAW_MODE_BASE+NUM_DRAW_MODES) {
		m_config.drawMode = id - ID_DRAW_MODE_BASE;
	}
}

void ConfigDialog::OnChangeRes(wxSpinEvent &evt) {
	int id = evt.GetId();
	int pos = evt.GetPosition();
	if (id == ID_CARTOON_RES) {
		m_config.cartoonRes = pos;
	}
	if (id == ID_SPHERE_RES) {
		m_config.sphereRes = pos;
	}
	if (id == ID_CYL_RES) {
		m_config.cylRes = pos;
	}
}

void ConfigDialog::OnUseDefault(wxCommandEvent& evt) {
	unsigned int id = evt.GetId();
	Config defaultConfig;
	defaultConfig.SetDefault();
	switch (id) {
		case ID_USE_DEFAULT_COLORS:
			for (unsigned int i=0; i<NUM_COLORS; i++) {
				wxColourPickerCtrl *picker = (wxColourPickerCtrl*) colorPanel->FindWindow(i + NUM_CONFIG_STRINGS);
				if (picker) {
					picker->SetColour(WX_COLOR(defaultConfig.firstColor[i]));
				}
				m_config.firstColor[i] = defaultConfig.firstColor[i];
			}
			break;
		default:
			break;
	}
}

int Config::ShowModal(wxWindow *parent) {
	ConfigDialog dlg(*config);
	if (dlg.Create(parent)) {
		int res = dlg.ShowModal();
		if (res == wxID_OK) {
			*config = dlg.m_config;
			config->Save();
			return wxID_OK;
		}
	}
	return wxID_CANCEL;
}

